package org.ctd.springframework.beans.utils.bean;

import com.ctd.springframework.util.Assert;
import com.ctd.springframework.util.reflection.ReflectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.ctd.springframework.beans.exception.bean.instantiation.BeanInstantiationException;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;

/**
 * BeanUtils
 *
 * @author chentudong
 * @date 2020/9/11 16:10
 * @since 1.0
 */
public abstract class BeanUtils {
    private static final Log LOGGER = LogFactory.getLog(BeanUtils.class);

    public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
        Assert.notNull(clazz, "Class must not be null");
        //判断类是否是接口
        if (clazz.isInterface()) {
            throw new BeanInstantiationException(clazz, "Specified class is an interface");
        }
        //实例化bean
        try {
            Constructor<T> declaredConstructor = clazz.getDeclaredConstructor();
            if (Objects.isNull(declaredConstructor)) {
                throw new BeanInstantiationException(clazz, "No default constructor found");
            }
            return instantiateClass(declaredConstructor);
        } catch (NoSuchMethodException ex) {
            ex.printStackTrace();
            throw new BeanInstantiationException(clazz, "No default constructor found", ex);
        }
    }

    /**
     * 构造方法实例化对象
     *
     * @param declaredConstructor declaredConstructor
     * @param <T>                 <T>
     * @return T
     */
    public static <T> T instantiateClass(Constructor<T> declaredConstructor, Object... args) throws BeanInstantiationException {
        Assert.notNull(declaredConstructor, "Constructor must not be null");
        ReflectionUtils.makeAccessible(declaredConstructor);
        try {
            return declaredConstructor.newInstance(args);
        } catch (InstantiationException ex) {
            throw new BeanInstantiationException(declaredConstructor, "Is it an abstract class?", ex);
        } catch (IllegalAccessException ex) {
            throw new BeanInstantiationException(declaredConstructor, "Is the constructor accessible?", ex);
        } catch (IllegalArgumentException ex) {
            throw new BeanInstantiationException(declaredConstructor, "Illegal arguments for constructor", ex);
        } catch (InvocationTargetException ex) {
            throw new BeanInstantiationException(declaredConstructor, "Constructor threw exception", ex.getTargetException());
        }
    }
}